/*
 * anima/locks.hh - Locks implementation on top of atomic variables.
 * Copyright (C) 2007  Mikhail Vorozhtsov
 * See the LICENSE section of the README file for details.
 */

/* $Id$ */

#ifndef HEADER_ANIMA_LOCKS_HH
#define HEADER_ANIMA_LOCKS_HH

#include HEADER (anima,atomic.hh)

START_NAMESPACE (anima)

template<typename T, u8_t BIT = 0>
struct spin_lock_t
{
private:
  atomic_t<T> value;

public:
  INLINE
  spin_lock_t (bool locked_p = false)
  {
    value = locked_p ? ((T) 1 << BIT) : 0;
  }

public:
  INLINE
  static void
  clear (T &x)
  {
    x &= ((T) 1 << BIT);
  }

  INLINE
  bool 
  try_lock ()
  {
    return !value.set_bit (BIT, true);
  }

  INLINE
  void
  lock ()
  {
    while (!try_lock ())
      ;
  }

  INLINE
  void
  unlock ()
  {
    value.set_bit (BIT, false);
  }
};

typedef spin_lock_t<u8_t> spin_lock8_t;
typedef spin_lock_t<u16_t> spin_lock16_t;
typedef spin_lock_t<u32_t> spin_lock32_t;
typedef spin_lock_t<u64_t> spin_lock64_t;

template <typename T, u8_t LO_BIT = 0, u8_t HI_BIT = sizeof (T) * 8 - 2,
          u8_t W_BIT = sizeof (T) * 8 - 1>
struct rw_lock_t
{
private:
  static const T ONE = (T) 1 << LO_BIT;
  static const T HALF = (T) 1 << HI_BIT;
  static const T MASK = (((T) -1 >> LO_BIT) << LO_BIT)
                        & ((T) -1 >> (sizeof (T) * 8 - 1 - HI_BIT));
  static const T W = (T) 1 << W_BIT;

private:
  atomic_t<T> value;

public:
  INLINE
  rw_lock_t () : value (0)
  {
  }

public:
  INLINE
  static void
  clear (T &x)
  {
    x &= MASK | W;
  }

  INLINE
  bool
  try_rlock (bool agressive_p = false)
  {
    if (!agressive_p && value.get () & W)
      return false;

    if ((value.add (ONE) & MASK) >= HALF)
      {
        value.sub (ONE);
        return false;
      }

    return true;
  }

  INLINE
  void
  rlock (bool agressive_p = false)
  {
    while (!try_rlock (agressive_p))
      ;
  }

  INLINE
  void
  unrlock ()
  {
    value.sub (ONE);
  }

  INLINE
  bool
  try_wlock (bool rlocked_p = false)
  {
    u64_t save = value.get () & ~MASK;

    if (rlocked_p)
      return value.set_if_eq ((save & ~W) | HALF, save | ONE);
    else
      return value.set_if_eq ((save & ~W) | HALF, save);
  }

  INLINE
  void
  wlock (bool agressive_p = true, bool rlocked_p = false)
  {
    while (!try_wlock (rlocked_p))
      {
        if (agressive_p)
          value.set_bit (W_BIT);
      }
  }

  INLINE
  void
  unwlock (bool rlock_p = false)
  {
    if (rlock_p)
      value.add (ONE);
    value.set_bit (HI_BIT, false);
  }
};

typedef rw_lock_t<u8_t> rw_lock8_t;
typedef rw_lock_t<u16_t> rw_lock16_t;
typedef rw_lock_t<u32_t> rw_lock32_t;
typedef rw_lock_t<u64_t> rw_lock64_t;

END_NAMESPACE

#endif /* HEADER_ANIMA_LOCKS_HH */

